/**
 * 广寒宫
 * 网址:www.guanghangong.xyz
 */
package org.moon.framework.autoconfigure.scp;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.moon.framework.autoconfigure.exception.domain.MoonException;
import org.moon.framework.autoconfigure.scp.prop.ScpProperties;
import org.moon.framework.autoconfigure.utils.ToolUtils;

import java.io.*;
import java.time.LocalDate;

/**
 * scp 上传文件
 * @author moon
 */
@Slf4j
public class ScpClient {

	private ScpProperties scpProperties;
	private Connection conn;

	public ScpClient(ScpProperties scpProperties){
		try {
			this.scpProperties = scpProperties;
			if(scpProperties.getIsLocal()){//文件存储在本地，不需要开启scp
				log.info("文件存储在本地，不需要开启scp");
			}else{
				login();
			}
		} catch (IOException e) {
			log.error(e.getMessage(),e);
		}
	}
	
	/**
	 * 登录服务器
	 */
	private void login()throws IOException{
		long t = System.currentTimeMillis();
		log.info("开启初始化scp...");
		conn = new Connection(scpProperties.getIp(),scpProperties.getPort());
		conn.connect();
		boolean isAuthenticated = conn.authenticateWithPassword(scpProperties.getUsername(), scpProperties.getPassword());
		if (isAuthenticated == false) {
			throw new MoonException("scp authentication failed");
		}
		log.info("scp 初始化完成,用时{}",System.currentTimeMillis()-t);
	}

	/**
	 * 下载文件
	 */
	public void getFile(String remoteFile, String localTargetDirectory) {
		try {
			if(scpProperties.getIsLocal()){//文件存储在本地，不需要开启scp
				FileUtils.copyFileToDirectory(new File(remoteFile), new File(localTargetDirectory));
				return;
			}
			if(conn==null || !conn.isAuthenticationComplete()){
				login();
			}
			SCPClient client = new SCPClient(conn);
			client.get(remoteFile, localTargetDirectory);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
	}

	/**
	 * @param localFile 本地文件(新文件名将由uuid生成)
	 * @param type 业务类型 <br>
	 * 
	 * 如果配制的 @link ScpProperties.getIp 为当前机器的ip，则直接复制或者改名到指定目录下
	 */
	public String putFile(String localFile, String type) {
		return putFile(localFile, true, type);
	}

	/**
	 * @param localFile 本地文件
	 * @param reName 是否重命名
	 * @param type 业务类型 <br>
	 * 
	 * 如果配制的 @link ScpProperties.getIp 为当前机器的ip，则直接复制或者改名到指定目录下
	 */
	public String putFile(String localFile, boolean reName, String type) {
		LocalDate date = LocalDate.now();
		String fileType = localFile.substring(localFile.lastIndexOf(".") + 1);// 文件后缀
		String remoteFileDir = type + "/" + date.getYear() + "/" + date.getMonthValue() + "/" + date.getDayOfMonth() + "/" + fileType;
		String fullRemoteFileDir = scpProperties.getBaseDir() + "/" + remoteFileDir;
		String fileName = null;
		if(!reName){
			fileName = new File(localFile).getName();
		}else{
			fileName = ToolUtils.getLowerUUID() + "." + fileType;
		}
		if(scpProperties.getIsLocal()){//同一台机器直接写入指定磁盘即可
			String newFile = fullRemoteFileDir+"/"+fileName;
			try {
				String typeDir = scpProperties.getBaseDir() + "/" + type;
				File dir = new File(fullRemoteFileDir);
				if(!dir.exists()){
					FileUtils.forceMkdir(new File(fullRemoteFileDir));
				}
				FileUtils.copyFile(new File(localFile) , new File(newFile));
//				Runtime.getRuntime().exec("chmod 755 `find "+typeDir+" -type d`");//TODO 为什么不启作用
				Runtime.getRuntime().exec("chmod -R 755 "+typeDir);
//				Runtime.getRuntime().exec("chmod 644 " + newFile); // chmod -R 会将子目录下所有文件也都修改了，所以这条命令意义不大，暂时还不清楚将文件的权限修改为644有什么别的影响？
			} catch (IOException e) {
				log.error(e.getMessage(), e);
				return StringUtils.EMPTY;
			}
			return remoteFileDir+"/"+fileName;
		}
		//远程传输 scp
		try {
			if(conn==null || !conn.isAuthenticationComplete()){
				login();
				log.warn("scp 重新登录...");
			}
			try {
				Session sess = conn.openSession();
				sess.close();
			} catch (Exception e) {
				login();
				log.warn("scp 重新登录...");
			}
			//生成目录
			execShellScript("mkdir -p " + fullRemoteFileDir);
			SCPClient client = new SCPClient(conn);
			client.put(localFile,fileName,fullRemoteFileDir, "0644");
			return remoteFileDir+"/"+fileName;
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		return StringUtils.EMPTY;
	}

	/**
	 * 执行脚本
	 */
	private void execShellScript(String cmd)throws IOException {
		Session sess = null;
		InputStream stdout = null;
		BufferedReader br = null;
		StringBuffer buffer = new StringBuffer("exec result:");
		buffer.append(System.getProperty("line.separator"));// 换行
		try {
			sess = conn.openSession();
			sess.execCommand(cmd);
			stdout = new StreamGobbler(sess.getStdout());
			br = new BufferedReader(new InputStreamReader(stdout));
			while (true) {
				String line = br.readLine();
				if (line == null)break;
				buffer.append(line);
				buffer.append(System.getProperty("line.separator"));// 换行
			}
		}catch(Exception e){
			log.error(e.getMessage(),e);
		} finally {
			try {
				if(stdout!=null)stdout.close();
				if(br!=null)br.close();
			} catch (IOException e) {
				log.error(e.getMessage(), e);
			}
			if(sess!=null)sess.close();
		}
	}
	
	public void clean(){
		if(conn!=null && conn.isAuthenticationComplete()){
			conn.close();
		}
	}
}